﻿using System;
using System.Globalization;
using System.Windows;
using Notoric.Commanding.CommandBase;

namespace Notoric.ViewModel.Elements.CWNotation
{
    /// <summary>
    /// A common base class for adapters of <see cref="Model.Elements.CWNotation.Arc"/> instances.
    /// </summary>
    public abstract class Arc : Connector
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Arc"/> class.
        /// </summary>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="System"/>
        /// </para>
        /// <para>
        /// The system that directly owns this arc.
        /// </para>
        /// </param>
        /// <param name="adaptedElement">
        /// <para>
        /// Type: <see cref="Arc"/>
        /// </para>
        /// <para>
        /// The adapted element.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="adaptedElement"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="NullReferenceException">
        /// The <paramref name="owner"/> is <see langword="null"/>.
        /// </exception>
        protected Arc(System owner, Model.Elements.CWNotation.Arc adaptedElement)
            : base(owner, adaptedElement)
        {
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Creates an undoable command for setting the <see cref="BezierPoint1"/> property.
        /// </summary>
        /// <param name="point">
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The point.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for execution.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetBezierPoint1Command(Point point)
        {
            var pt = new Point(point.X - FindOwner<System>().Left, point.Y - FindOwner<System>().Top);
            var modelPoint = (Model.Elements.Point)StructuresConverter.ConvertBack(pt, typeof(Model.Elements.Point), null, CultureInfo.CurrentCulture);

            return new SetterCommand<Model.Elements.Point>(this, "BezierPoint1", modelPoint, UndoStack, RedoStack)
            {
                Name = "Change Arc"
            };
        }

        /// <summary>
        /// Creates an undoable command for setting the <see cref="BezierPoint2"/> property.
        /// </summary>
        /// <param name="point">
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The point.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for execution.
        /// </para>
        /// </returns>
        public UndoableCommandBase CreateSetBezierPoint2Command(Point point)
        {
            var pt = new Point(point.X - FindOwner<System>().Left, point.Y - FindOwner<System>().Top);
            var modelPoint = (Model.Elements.Point)StructuresConverter.ConvertBack(pt, typeof(Model.Elements.Point), null, CultureInfo.CurrentCulture);

            return new SetterCommand<Model.Elements.Point>(this, "BezierPoint2", modelPoint, UndoStack, RedoStack)
            {
                Name = "Change Arc"
            };
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the coordinates of the first control point of the Bézier curve.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The coordinates (in device independent pixels) of the first
        /// control point of the Bézier curve relative to the top-left corner
        /// of the owner section.
        /// </para>
        /// </value>
        public Point BezierPoint1
        {
            get 
            { 
                var basePoint = (Point)StructuresConverter.Convert(GetPropertyValue<Model.Elements.Point>("BezierPoint1"), typeof(Point), null, CultureInfo.CurrentCulture);
                
                return new Point(basePoint.X + FindOwner<System>().Left, basePoint.Y + FindOwner<System>().Top);
            }
            set
            {
                if (UnsavedChanges.ContainsKey("BezierPoint1"))
                {
                    var pt = new Point(value.X - FindOwner<System>().Left, value.Y - FindOwner<System>().Top);
                    var cmd = (SetterCommand<Model.Elements.Point>)UnsavedChanges["BezierPoint1"];
                    cmd.Value = (Model.Elements.Point)StructuresConverter.ConvertBack(pt, typeof(Model.Elements.Point), null, CultureInfo.CurrentCulture);
                }
                else
                    UnsavedChanges["BezierPoint1"] = CreateSetBezierPoint1Command(value);

                RaisePropertyChanged("BezierPoint1");
            }
        }

        /// <summary>
        /// Gets or sets the coordinates of the second control point of the Bézier curve.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The coordinates (in device independent pixels) of the second
        /// control point of the Bézier curve relative to the top-left corner
        /// of the owner section.
        /// </para>
        /// </value>
        public Point BezierPoint2
        {
            get 
            { 
                var basePoint = (Point)StructuresConverter.Convert(GetPropertyValue<Model.Elements.Point>("BezierPoint2"), typeof(Point), null, CultureInfo.CurrentCulture);

                return new Point(basePoint.X + FindOwner<System>().Left, basePoint.Y + FindOwner<System>().Top);
            }
            set
            {
                if (UnsavedChanges.ContainsKey("BezierPoint2"))
                {
                    var pt = new Point(value.X - FindOwner<System>().Left, value.Y - FindOwner<System>().Top);
                    var cmd = (SetterCommand<Model.Elements.Point>)UnsavedChanges["BezierPoint2"];
                    cmd.Value = (Model.Elements.Point)StructuresConverter.ConvertBack(pt, typeof(Model.Elements.Point), null, CultureInfo.CurrentCulture);
                }
                else
                    UnsavedChanges["BezierPoint2"] = CreateSetBezierPoint2Command(value);

                RaisePropertyChanged("BezierPoint2");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this arc is selected.
        /// </summary>
        /// <value>
        /// 	<see langword="true"/> if this instance is selected; otherwise, <see langword="false"/>.
        /// </value>
        public bool IsSelected
        {
            get { return isSelected; }
            set 
            {
                isSelected = value;

                RaisePropertyChanged("IsSelected");
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the value of the <see cref="IsSelected"/> property.
        /// </summary>
        private bool isSelected = false;

        #endregion
    }
}
